/**
 * Copyright (C) 2025 pony working team
 * SPDX-License-Identifier: MulanPSL-2.0
 */

/* includes (local) ----------------------------------------------------------*/
#include <hal_key.h>
/* includes (standard library, system) ---------------------------------------*/
/* includes (other library) --------------------------------------------------*/
/* includes (project) --------------------------------------------------------*/
#include <x_config.h>

/* defines -------------------------------------------------------------------*/
/* typedefs ------------------------------------------------------------------*/
/* macro ---------------------------------------------------------------------*/
/* variables (extern) --------------------------------------------------------*/
/* variables (local) ---------------------------------------------------------*/

static hal_key_t *key_header;
static key_event_cb event_cb;

/* variables (global) --------------------------------------------------------*/
/* inlines -------------------------------------------------------------------*/
/* functions -----------------------------------------------------------------*/

static int __key_event_handler(uint8_t id, uint8_t event)
{
    if (event_cb != NULL) {
        return event_cb(id, event);
    }

    return -1;
}

static void __key_handler(hal_key_t *key)
{
    uint8_t current_state = key->get_state(key->id);

    switch (key->state) {
    case HAL_KEY_STATE_IDLE:
        if (current_state == HAL_KEY_STATE_PRESSED) {
            key->state = HAL_KEY_STATE_PRESSED;
            key->click_cnt = 0;
            key->tick_cnt = 1;
        }
        break;
    case HAL_KEY_STATE_PRESSED:
        if (current_state == HAL_KEY_STATE_RELEASED) {
            key->state = HAL_KEY_STATE_IDLE;
            __key_event_handler(key->id, HAL_KEY_EVENT_UP);
        } else if (key->tick_cnt < CONFIG_KEY_DEBOUNCE_TICK_CNT) {
            ++key->tick_cnt;
        } else {
            key->state = HAL_KEY_STATE_SHORT_PRESSED;
            __key_event_handler(key->id, HAL_KEY_EVENT_DOWN);
        }
        break;
    case HAL_KEY_STATE_SHORT_PRESSED:
        if (current_state == HAL_KEY_STATE_RELEASED) {
            key->state = HAL_KEY_STATE_RELEASED;
            ++key->click_cnt;
        } else if (key->tick_cnt < CONFIG_KEY_LONG_PRESSED_TICK_CNT) {
            ++key->tick_cnt;
        } else {
            key->tick_cnt = 0;
            key->state = HAL_KEY_STATE_LONG_PRESSED;
            __key_event_handler(key->id, HAL_KEY_EVENT_LONG_PRESS);
        }
        break;
    case HAL_KEY_STATE_RELEASED:
        ++key->tick_cnt;
        if (key->tick_cnt >= CONFIG_KEY_CLICK_TICK_CNT) {
            if (key->click_cnt == 1) {
                __key_event_handler(key->id, HAL_KEY_EVENT_CLICK);
            } else if (key->click_cnt == 2) {
                __key_event_handler(key->id, HAL_KEY_EVENT_DOUBLE_CLICK);
            } else if (key->click_cnt == 3) {
                __key_event_handler(key->id, HAL_KEY_EVENT_TRIPLE_CLICK);
            }
            key->state = HAL_KEY_STATE_IDLE;
        } else if (current_state == HAL_KEY_STATE_PRESSED) {
            key->state = HAL_KEY_STATE_PRESSED;
            key->tick_cnt = 1;
        }
        break;
    case HAL_KEY_STATE_LONG_PRESSED:
        if (current_state == HAL_KEY_STATE_RELEASED) {
            key->state = HAL_KEY_STATE_IDLE;
            __key_event_handler(key->id, HAL_KEY_EVENT_UP);
        } else {
            if (key->tick_cnt < CONFIG_KEY_LONG_PRESSED_TICK_CNT) {
                ++key->tick_cnt;
            } else {
                key->tick_cnt = 0;
                __key_event_handler(key->id, HAL_KEY_EVENT_LONG_PRESS);
            }
        }
        break;
    default:
        break;
    }
}

void hal_key_set_event_cb(key_event_cb cb)
{
    event_cb = cb;
}

void hal_key_attach(hal_key_t *key)
{
    if (key == NULL || key->get_state == NULL) {
        return;
    }
    key->state = HAL_KEY_STATE_IDLE;
    key->click_cnt = 0;
    key->tick_cnt = 0;

    if (key_header == NULL) {
        key_header = key;
    } else {
        hal_key_t *current = key_header;
        while (current->next != NULL) {
            current = current->next;
        }
        current->next = key;
    }
    key->next = NULL;
}

void hal_key_scan(void)
{
    hal_key_t *key = key_header;

    while (key != NULL) {
        __key_handler(key);
        key = key->next;
    }
}
